// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.IO;

using Microsoft.TeamFoundation.Migration.Toolkit.Linking;
using WIT=Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace Microsoft.TeamFoundation.Migration.Toolkit.Wit
{
    /// <summary>
    /// Creates a work item store.
    /// </summary>
    public interface IMigrationWorkItemStoreFactory
    {
        /// <summary>
        /// Creates a work item store.
        /// </summary>
        /// <returns>Provider specific work item store.</returns>
        IMigrationWorkItemStore CreateWorkItemStore();
    }

    /// <summary>
    /// WIT-specific end point.
    /// </summary>
    public interface IWorkItemTrackingEndpoint : IMigrationWorkItemStoreFactory
    {
        /// <summary>
        /// Provides a unique, user-friendly name for this endpoint
        /// </summary>
        string FullName { get; }

        /// <summary>
        /// Provides access to the write queue configuration.
        /// </summary>
        QueueConfiguration WriteQueueConfig { get; set; }

        /// <summary>
        /// Provides access to the current session
        /// </summary>
        WorkItemTrackingSession Session { get; set; }
    }

    /// <summary>
    /// Describes kinds of work item's data
    /// </summary>
    [Flags]
    public enum MigrationWorkItemData
    {
        None = 0x00,                    // No data
        All = Revisions | Attachments | Links,  // All known kinds

        Revisions = 0x01,               // Historical revisions
        Attachments = 0x02,             // File attachments
        Links = 0x04,                   // Links between work items
    }

    /// <summary>
    /// A place where work items are stored. The engine uses this interface to obtain and update
    /// work items that require synchronization.
    /// </summary>
    public interface IMigrationWorkItemStore
    {
        /// <summary>
        /// Gets items scheduled for synchronization.
        /// </summary>
        /// <param name="highWatermark">High watermark string. 
        ///         The engine saves this for each session.  
        ///         The value passed in is the value from the last 
        ///         time the session was synch'ed.  It should be updated 
        ///         with whatever you want to filter by next time.  
        ///         It could be a timestamp or whatever the product needs 
        ///         to filter out old changes so the tool doesn't repeatedly 
        ///         try to sync up work items that have not changed since 
        ///         the last sync.</param>
        /// <returns>Collection of items</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#")]
        IEnumerable<IMigrationWorkItem> GetItems(
            ref string highWatermark);

        /// <summary>
        /// Obtains work items with given ids.
        /// </summary>
        /// <param name="ids">Work items' ids</param>
        /// <returns>Results (one per each item)</returns>
        GetWorkItemResult[] GetItems(
            string[] ids);

        /// <summary>
        /// Submits a batch of independent atomic updates.
        /// </summary>
        /// <param name="updates">Updates</param>
        /// <returns>Result for every update. The number of updates should match the number of results, or
        /// an exception will be thrown. Results should go in the same order as updates.</returns>
        UpdateResult[] Update(
            IWorkItemUpdate[] updates);

        /// <summary>
        /// Verifies all settings before synchronization.
        /// </summary>
        /// <param name="session">Context in which the synchronization process will be running</param>
        /// <param name="tfsStore">TFS store on the opposite side</param>
        void VerifySettings(WorkItemTrackingSession session, WIT.WorkItemStore tfsStore);

        /// <summary>
        /// Checks whether a user with the given name can be found in the work item store.
        /// </summary>
        /// <param name="name">Name</param>
        /// <returns>True if such user exists</returns>
        bool IsValidUser(
            string name);

        /// <summary>
        /// Checks whether a field is valid for a work item type.
        /// </summary>
        /// <param name="workItemType">Work item type</param>
        /// <param name="fieldName">Field name</param>
        /// <returns>True if the field is valid for the work item type.</returns>
        bool IsValidField(
            string workItemType,
            string fieldName);

        /// <summary>
        /// Gets names of all non-audited (aka flat) fields for the given work item type.
        /// </summary>
        /// <param name="workItemType">Work item types</param>
        /// <returns>Field names</returns>
        IEnumerable<string> GetFlatFields(string workItemType);

        /// <summary>
        /// Returns set of flags describing supported kinds of work item data.
        /// </summary>
        MigrationWorkItemData Flags { get; }

        /// <summary>
        /// Opens another connection to the work item store. The engine calls this method
        /// when it needs to open another connection in analysis or write queues.
        /// </summary>
        /// <returns>Work item store</returns>
        IMigrationWorkItemStore Reopen();

        /// <summary>
        /// Synchronizes own metadata with the given TFS project.
        /// </summary>
        /// <param name="project">TFS project to synchronize with</param>
        void SynchronizeMetadata(WIT.Project project);

        /// <summary>
        /// Synchronizes system's work item type with the type from TFS side. The engine calls this
        /// method for every work item type obtained from items being synchronized. Authors can implement
        /// that method to allow just-in-type synchronization of work item types between both sides.
        /// </summary>
        /// <param name="typeName">Name of the type in the system</param>
        /// <param name="tfsTypeName">Name of the type in TFS</param>
        /// <param name="types">Collection of TFS work item types</param>
        void SynchronizeWorkItemType(
            string typeName,
            string tfsTypeName,
            WIT.WorkItemTypeCollection types);

        /// <summary>
        /// Refreshes the store. This method is called by the engine after each synchronization pass
        /// to bring the store up to date.
        /// </summary>
        void Refresh();

        /// <summary>
        /// Closes the work item store. The engine calls this method when the store is no longer in use.
        /// </summary>
        void Close();

        /// <summary>
        /// Gets change list factory.
        /// </summary>
        IWorkItemUpdatePackageFactory ChangeListFactory { get; }

        /// <summary>
        /// Gets comparer used with string values.
        /// </summary>
        StringComparer StringValueComparer { get; }

        /// <summary>
        /// Gets comparer used with field names.
        /// </summary>
        StringComparer FieldNameComparer { get; }

        /// <summary>
        /// Gets comparer used with work item ids.
        /// </summary>
        StringComparer IdComparer { get; }

        /// <summary>
        /// Gets name of the system this interface implements. This value is system-specific;
        /// for TFS system it is 'TFS'.
        /// </summary>
        string SystemName { get; }

        /// <summary>
        /// Gets system-specific name of the work item store.
        /// </summary>
        string StoreName { get; }
    }

    /// <summary>
    /// Abstract work item.
    /// </summary>
    public interface IMigrationWorkItem: ILinkContainer
    {
        /// <summary>
        /// Gets watermark of the work item type
        /// </summary>
        Watermark Watermark { get; }

        /// <summary>
        /// Gets name of the work item type.
        /// </summary>
        string WorkItemType { get; }

        /// <summary>
        /// Tells what kinds of data the work item contains.
        /// </summary>
        MigrationWorkItemData Flags { get; }

        /// <summary>
        /// Gets historical revisions beginning with a revision with the given index.
        /// </summary>
        /// <param name="revision">Revision</param>
        /// <returns>List of revisions</returns>
        ReadOnlyCollection<MigrationRevision> GetRevisions(
            int revision);

        /// <summary>
        /// Gets the latest values for the specified fields. The method is currently used only by flat
        /// fields synchronization.
        /// </summary>
        /// <param name="fieldNames">Fields names</param>
        /// <returns>Collection of values</returns>
        IEnumerable<MigrationField> GetLatestValues(
            IEnumerable<string> fieldNames);

        /// <summary>
        /// Gets a list of files attached to a work item.
        /// </summary>
        /// <returns>Dictionary of attachments.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        ReadOnlyCollection<IMigrationFileAttachment> GetFileAttachments();

        /// <summary>
        /// The method is called by the engine before the change list for the item on the opposite
        /// side gets translated into the list of updates. It allows adding some custom data into the
        /// change list.
        /// </summary>
        /// <param name="target">Change list for updating the target change list</param>
        void OnTargetUpdate(
            IWorkItemUpdatePackage target);
    }

    /// <summary>
    /// Represents a file attached to a work item.
    /// </summary>
    public interface IMigrationFileAttachment
    {
        /// <summary>
        /// Returns the name of the file.
        /// </summary>
        string Name { get; }

        /// <summary>
        /// Returns the file length/size.
        /// </summary>
        long Length { get;}

        /// <summary>
        /// Returns the timestamp when the file was created as a UTC datetime.
        /// </summary>
        DateTime UtcCreationDate { get; }

        /// <summary>
        /// Returns the timestamp when the file was last updated as a UTC datetime.
        /// </summary>
        DateTime UtcLastWriteDate { get; }

        /// <summary>
        /// Returns a comment about the file.
        /// </summary>
        string Comment { get; }

        /// <summary>
        /// Gets the contents of the file when needed for comparison.
        /// </summary>
        /// <returns>Contents of the file.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        Stream GetFileContents();
    }

    /// <summary>
    /// Factory for creating update packages.
    /// </summary>
    public interface IWorkItemUpdatePackageFactory
    {
        /// <summary>
        /// Creates an update package for modifying an existing work item.
        /// </summary>
        /// <param name="source">Source work item</param>
        /// <param name="item">Work item that should be modified</param>
        /// <returns>Update package object</returns>
        IWorkItemUpdatePackage CreateUpdatePackage(
            IMigrationWorkItem source,
            IMigrationWorkItem item);

        /// <summary>
        /// Creates an update package for a new work item of the given type.
        /// </summary>
        /// <param name="source">Source work item</param>
        /// <param name="workItemType">Work item type</param>
        /// <returns>Update package object</returns>
        IWorkItemUpdatePackage CreateUpdatePackage(
            IMigrationWorkItem source,
            string workItemType);
    }

    /// <summary>
    /// Update package interface. Represents a collection of operations on a single work item type.
    /// </summary>
    public interface IWorkItemUpdatePackage
    {
        /// <summary>
        /// Provides access to the collection of revisions
        /// </summary>
        Collection<MigrationRevision> Revisions { get; }

        /// <summary>
        /// Provides access to the collection of file attachment changes.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        Collection<SyncActionContainer<IMigrationFileAttachment>> FileAttachmentOperations { get; }

        /// <summary>
        /// Pending link operations.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        Collection<SyncActionContainer<ILink>> LinkOperations { get; }

        /// <summary>
        /// Translates content into the list of atomic updates.
        /// </summary>
        /// <returns>Atomic changes</returns>
        IEnumerable<IWorkItemUpdate> Translate();
    }

    /// <summary>
    /// Atomic update to a single work item type. Change list translates its content into a set
    /// of updates which get submitted one by one into the target work item store.
    /// </summary>
    public interface IWorkItemUpdate
    {
        /// <summary>
        /// Returns revision of the source item the update is trying to migrate. For changes not
        /// associated with a specific revision (links, attachments only) this property should
        /// return -1. Every revision must be migrated with a single update statement: the engine
        /// uses that property to mark migration of a certain revision as complete.
        /// </summary>
        int SourceRevision { get; }

        /// <summary>
        /// Sets the revision the update should apply to.
        /// </summary>
        /// <param name="watermark">Target revision</param>
        void SetTarget(
            Watermark watermark);
    }

    /// <summary>
    /// Interface for transforming fields' values.
    /// </summary>
    public interface IFieldValueTransformer
    {
        /// <summary>
        /// Gets a transformed value of the given field.
        /// </summary>
        /// <param name="fields">Fields</param>
        /// <param name="field">Field whose value should be transformed</param>
        /// <param name="value">Field's current value</param>
        /// <returns>Transformed field value</returns>
        object GetTransformedValue(WIT.FieldCollection fields, WIT.Field field, object value);
    }
}
